#include <iostream>
#include <string>
#include <fstream>
#include <map>
#include <set>
#include <sstream>
#include <cctype>
#include <vector>
#include <cstdlib>

using namespace std;



/* Function: Loadwords();
* Usage: multimap<size_t, string>& result = LoadWords();
* -------------------------------------------------
* Returns a multimap from the words of dictionary. It stores worlength as key.
*/
void Loadwords(multimap<size_t, string > & wordlistmap);

/*	Flag to check whether success or failure */
#define SUCCESS 1
#define FAIL 0

struct UserOptions {
		int wordlength;			// 	Initial length of world selected by user
		int numberofguess;		//	Total guess 
		char remainwordoption;	//	For seceting whether user wants to display remaining word to be displayed or not
		char guesschar;			// 	Guess character given by user
		int correctguess;		//	Toatal number of correct guess made
};		

string ansString = "";



string GetLine() {
	string result;
	getline(cin, result);
	return result;
}




void Loadwords(multimap<size_t, string > & wordlistmap) {
	ifstream input("dictionary.txt"); // No error checking for brevity's sake
	
	size_t lenght;
	
	/* Keep reading strings out of the file until we cannot read any more.
	* After reading each string, store it in the wordlist multimap. We can either
	* use getline or the stream extraction operator here, but the stream
	* extraction operator is a bit more general.
	*/

	string word;
	
	while (input >> word)
		 wordlistmap.insert(make_pair(word.length(),word));
	
}



int GetInteger() {
	while(true) { // Read input until user enters valid data
	stringstream converter;
	converter << GetLine();
	
	/* Try reading an int, continue if we succeeded. */
	int result;
	
	if(converter >> result) {
		char remaining;
		if(converter >> remaining) // Something's left, input is invalid
			cout << "Unexpected character: " << remaining << endl;
		else
			return result;
	} else
		cout << "Please enter an integer." << endl;
	}
}


void GetLengthOptions(UserOptions & usropt, multimap<size_t, string >& wordlistmap){
	while(true){
		cout<<"Please enter a valid word length :"<<endl;
		usropt.wordlength = GetInteger();
		
		if(wordlistmap.find(usropt.wordlength) != wordlistmap.end())
			break;
	}		
}	

void GetGuessOptions(UserOptions & usropt ){
	cout<<"Please enter number of guess :"<<endl;
	usropt.numberofguess = GetInteger();
}

void GetRemainOptions(UserOptions & usropt){
	cout<<"Whether you want number of remaining words option or not \n If yes press y or Y \n if no press n or N : "<<endl;
	while(true){
		string str = GetLine();
		if(str.length()==1){
			usropt.remainwordoption = str[0];
			if(usropt.remainwordoption=='Y'||usropt.remainwordoption=='y'||usropt.remainwordoption=='n'||usropt.remainwordoption=='N')
				break;
			cout<<"Please enter a valid option: "<<endl;	
		}		
		else
			cout<<"Please enter a valid option: "<<endl;
	}			
}


void GetOption(UserOptions & usropt, multimap<size_t, string >& wordlistmap){
		GetLengthOptions(usropt, wordlistmap);
		GetGuessOptions(usropt);
		GetRemainOptions(usropt);
}
	

void FirstWordListing(UserOptions & usropt, vector<string >& wordlistvec, multimap<size_t, string >& wordlistmap){
		multimap<size_t, string >::iterator it,itlow,itup;
		
		itlow = wordlistmap.lower_bound (usropt.wordlength); 
		itup = wordlistmap.upper_bound (usropt.wordlength); 
		
		for(it = itlow; it!= itup; ++it )
				wordlistvec.push_back((*it).second);
		
		usropt.correctguess = 0;		
}		



void DisplayStatus(UserOptions & usropt, int size){
	
	cout<<"Attempts left : "<<usropt.numberofguess<<endl;
	if(toupper (usropt.remainwordoption) == 'Y' )
					cout<<"Size : "<<size<<endl;
					
	cout << ansString << endl;				
}	



void GetGuess(UserOptions & usropt){
	cout<<"Please enter an alphabet : ";
	while(true){
		string str = GetLine();
		if(str.length()==1 && isalpha(str[0])){
			usropt.guesschar = tolower( str[0] );
				break;
		}	
		else
			cout<<"Not valid optio! Please enter an alphabet: "<<endl;
	}			
	
}



void WordListing(UserOptions & usropt, vector<string >& wordlistvec){
	multimap <int ,string > tempmap;
	for( int i = 0; i <  wordlistvec.size(); i++){
			string tempstring;
			tempstring = wordlistvec[i];
			
			int len = tempstring.length();
			int flag = 0;
			
			for(size_t j = 0; j < len; j++){
				if(tempstring.at(j) == usropt.guesschar && ansString[j] == '_'){
					flag =1;
					tempmap.insert(make_pair(j+1, tempstring)); 
				}
			}
			
			if(!flag){
					tempmap.insert(make_pair(len+1, tempstring));	
					cout<<"Len+1 = "<<len+1<<endl;
			}					
	
	}				
	int max =0;
	int pos =1000;
	
	for( int i = 0; i <  usropt.wordlength+1; i++){
			int tempcount= (int)tempmap.count(i+1);
			cout<<"W Size : "<< tempcount<<endl;
			if(max < tempcount){
				max = tempcount;
				pos = i;
			}
	}
	
	multimap<int , string>::iterator it;
	wordlistvec.clear();
	
	system("clear");
	
	for (it=tempmap.equal_range(pos+1).first; it!=tempmap.equal_range(pos+1).second; ++it)
			wordlistvec.push_back((*it).second);
	
	tempmap.clear();
	if(pos < usropt.wordlength && wordlistvec[0].at(pos) == usropt.guesschar){
			ansString[pos] = toupper(wordlistvec[0].at(pos));	
			usropt.correctguess++;
			cout<<"Hi"<<endl;
	}	
		
	else{
		cout << "Sorry!!!!\nWrong guess word does not containt that alphabet please opt for another guess : "<<endl;		
		usropt.numberofguess--;
	}		
	
}			
			
				
		

void FinalDisplay(int resulT, string word){
	cout<<"Word was : "<<word<<endl;
	if(resulT)
		cout<<"WOW!!!!!!!!! Contulations u won the game......."<<endl;
	else
		cout<<"SORRY!!!! U lost the game......."<<endl;	
}



void GamePlay(UserOptions & usropt, vector<string >& wordlistvec, multimap<size_t, string >& wordlistmap ){
		FirstWordListing( usropt, wordlistvec, wordlistmap);
		
		for( int i = 0; i <  usropt.wordlength; i++)
				ansString+="_";
		
		int flag = FAIL;
		
		for( int i = 0; usropt.numberofguess; i++){
				DisplayStatus(usropt, wordlistvec.size());
				GetGuess(usropt);
				
				WordListing( usropt, wordlistvec);
				
				if(usropt.correctguess == usropt.wordlength){
						FinalDisplay(SUCCESS,wordlistvec[0]);
						flag = SUCCESS;
						break;
				}
		}
		if(!flag)
				FinalDisplay(FAIL,wordlistvec[0]);
}				
							
			

int main(){
	multimap<size_t, string > wordlistmap;
	vector<string > wordlistvec;
	
	Loadwords(wordlistmap);
	
	UserOptions usropt;
	
	GetOption(usropt, wordlistmap);
	usropt.correctguess = 0;
	GamePlay( usropt, wordlistvec, wordlistmap );
	return 0;
}	
